package com.hr;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.espirit.eap.manager.Department;
import com.espirit.eap.manager.DepartmentService;
import com.espirit.eap.sa.Employee;
import com.espirit.eap.util.DateUtils;
import com.espirit.finance.core.AcctTerm;
import com.espirit.finance.core.AcctTermService;
import com.googlecode.cswish.annotation.ParamScope;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.model.PageElement;
import com.googlecode.cswish.model.PageElements;
import com.googlecode.cswish.model.PageInfo;
import com.googlecode.cswish.model.ParameterMap;
import com.googlecode.cswish.model.Reference;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.struts.spring.CacheService;
import com.googlecode.cswish.struts.spring.GenericService;
import com.googlecode.cswish.struts.spring.SearchCallback;
import com.googlecode.cswish.util.bean.ModelUtil;

@Service
public class SchedualService {
	private static final Logger log = Logger.getLogger(SchedualService.class);
	@Resource
	HrSettingService hrSettingService;
	
	@Resource
	DepartmentService departmentService;
	
	@Resource
	GenericService genericService;
	
	@Resource
	SchedualDefineService schedualDefineService;
	
	@Resource
	private CacheService cacheService;
	
	@Resource
	AcctTermService acctTermService;
	
	//0930-1430,1800-2200
	static final String[] SCOPE_SEPARATOR_REGEXS = new String[]{"-", "[,，，]"};
	
	static final Integer DEFAULT_SCHEDUAL_DAYS = 7;
	
	static final Map<Integer, String> weekValues = new HashMap<Integer, String>();
	
	
	private List<Map<String,String>> srcWeekValues;
	
	@PostConstruct
	private void init() {
		srcWeekValues = new ArrayList<Map<String, String>>();;
		for(int i = 0; i < 7; i ++){
			Map<String, String > values = new HashMap<String, String>();
			values.put("id", Schedual.WEEK_PREFIX + i);
			values.put("name", weekValues.get(i));
			srcWeekValues.add(values);
		}
	}
	
	public SchedualService(){
		weekValues.put(0, "周日");
		weekValues.put(1, "周一");
		weekValues.put(2, "周二");
		weekValues.put(3, "周三");
		weekValues.put(4, "周四");
		weekValues.put(5, "周五");
		weekValues.put(6, "周六");
	}
	
	public List<Map<String,String>> getSrcWeekValues(){
		return srcWeekValues;
	}
	
	public  PageInfo search(final ParameterMap model, int start, int limit,
    		String sort, String dir, boolean newSearch, Reference<QLInfo> qlInfoRef,
    		@ParamScope(visible=false, value="#ps")PageElements ps ,boolean fromMenu
    		) {
		
		SearchCallback callback = departmentService.buildDeptDefaultSearchCallback(model, null, null);
		return genericService.search(model, start, limit, sort, dir, newSearch, qlInfoRef, callback, true, false);
	}
	
	public void _generateSchedual(Schedual model, @ParamScope(visible=false, value="#ps")PageElements ps){
		ps.setElementRequired("model.acctTerm.id", true);
		ps.setElementRequired("model.deptIds", true);
		
		AcctTerm currentAcctTerm = hrSettingService.getHrSetting().getCurrentAcctTerm();
		
//		int [] yearMonthDay = DateUtils.getCurrenYearMonthDay();
//		int year = yearMonthDay[0];
//		int month = yearMonthDay[1];
//		
//		if (!(currentAcctTerm.getYear().equals(year) && currentAcctTerm.getMonth().equals(month))){
//			currentAcctTerm = acctTermService.generateNext(currentAcctTerm);
//		}
		
		model.setAcctTerm(currentAcctTerm);
	}
	
	@Transactional
	public void generateSchedual(Schedual model){
		if (model == null) return ;
		String deptIds = model.getDeptIds();
		if (StringUtils.isEmpty(deptIds)) {
			throw new ApplicationException("请选择部门");
		}
		String [] ids = deptIds.split(",");
		Set<Integer> departmentIds = departmentService.getAllChildrenIds(ids);
		for(Integer id : departmentIds){
			generateSchedualRecordById(id, model.getAcctTerm().getId());
		}
	}
	
	public Schedual _update(Schedual model,
			@ParamScope(visible=false, value="#ps")PageElements ps){
		ps.setElementReadOnly("model.department.id", true);
		ps.setElementReadOnly("model.acctTerm.id", true);
		ps.setElementReadOnly("model.week", true);
		ps.setElementReadOnly("model.schedualItems.employee.id", false);
		Schedual schedual = genericService.load(model);
		Map<Integer, List<SchedualRecord>> children = new HashMap<Integer, List<SchedualRecord>>();
		for(SchedualRecord item : schedual.getChildren()){
			Employee emp = item.getEmployee();
			List<SchedualRecord> schedualRecords = children.get(emp.getId());
			if (schedualRecords == null){
				schedualRecords = new ArrayList<SchedualRecord>();
				children.put(emp.getId(), schedualRecords);
			}
			schedualRecords.add(item);
		}

		initDayLabel(children, ps);

		for(Integer empId : children.keySet()){
			List<SchedualRecord> schedualRecords = children.get(empId);
			SchedualItem schedualItem = new SchedualItem();
			Employee emp = new Employee();
			emp.setId(empId);
			emp = genericService.load(emp);
			schedualItem.setEmployee(emp);
			
			for(int i = 1; i <= schedualRecords.size(); i++){
				String scope = schedualRecords.get(i-1).getScope();
				ModelUtil.setProperty(schedualItem, "day" + i, scope); 
			}
			if (schedual.getSchedualItems() == null){
				schedual.setSchedualItems(new ArrayList<SchedualItem>());
			}
			schedual.getSchedualItems().add(schedualItem);
		}
		ModelUtil.copy(schedual, model);
		return model;
	}
	
	public void update(Schedual model){
		List<SchedualItem> schedualItems = model.getSchedualItems();
		model = genericService.load(model);
		Map<Integer, List<SchedualRecord>> children = new HashMap<Integer, List<SchedualRecord>>();
		for(SchedualRecord child : model.getChildren()){
			Employee emp = child.getEmployee();
			if (emp != null){
				List<SchedualRecord> records = children.get(emp.getId());
				if (records == null){
					records = new ArrayList<SchedualRecord>();
					children.put(emp.getId(), records);
				}
				records.add(child);
			}
		}
		
//		if (schedualItems.size() < children.size()){ //user delete the record
			List<Integer> removedEmployees = new ArrayList<Integer>();
			Set<Integer> empIds = children.keySet();
			for(Integer empId : empIds){
				boolean found = false;
				for(SchedualItem child : schedualItems){
					if (child.getEmployee().getId().equals(empId)){
						found =  true;
						break;
					}
				}
				if (!found){
					removedEmployees.add(empId);
				}
			}
			
			for(Integer empId : removedEmployees){
				Iterator<SchedualRecord> iter = model.getChildren().iterator();
				while(iter.hasNext()){
					SchedualRecord sr = iter.next();
					if (sr.getEmployee().getId().equals(empId)){
						iter.remove();
					}
				}
			}
//		}
		
		for(SchedualItem schedualItem : schedualItems){
			if (schedualItem == null || schedualItem.getEmployee() == null) continue;
			
			Integer empId = schedualItem.getEmployee().getId();
			List<SchedualRecord> schedualRecords = children.get(empId);
			if (schedualRecords != null){
				for(int i = 1 ; i <= schedualRecords.size() ; i++){
					String scope = (String) ModelUtil.getProperty(schedualItem, "day" + i);
					
					schedualDefineService.checkScope(scope);
					
					SchedualRecord schedualRecord = schedualRecords.get(i-1);
					schedualRecord.setScope(scope);
				}
			} else { // user add employee not exists in previous record
				int year = model.getAcctTerm().getYear();
				int month = model.getAcctTerm().getMonth();
				Integer[] startEndDay = getWeekDays(year, month, model.getWeek());
				int start = startEndDay[0];
				int end = startEndDay[1];
				int gap = start - 1;
				for(int i = start; i <= end; i++){
					String scope = (String) ModelUtil.getProperty(schedualItem, "day" + (i - gap) );
					
					schedualDefineService.checkScope(scope);
					SchedualRecord sr = new SchedualRecord();
					Employee emp = genericService.load(Employee.class, empId);
					sr.setEmployee(emp);
					sr.setParent(model);
					sr.setScope(scope);
					
					Calendar time = Calendar.getInstance();
					time.clear();
					time.set(year, month - 1 , i, 0, 0, 0);
					
					sr.setDutyOn(time.getTime());
					model.getChildren().add(sr);
				}
			}
		}
		genericService.merge(model);
	}
	
	private Integer[] getWeekDays(Integer year, Integer month, int weekNo) {
		// String date = "2013-09";
		String yearMonth = String.format("%04d", year) + "-" + String.format("%02d", month);
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM");
        Date date1 = null;
		try {
			date1 = dateFormat.parse(yearMonth);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date1);
        int days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        int count = 0;
        Integer[] startEndDay = new Integer[2];
        for (int i = 1; i <= days; i++) {
            DateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");
            Date date2 = null;
			try {
				date2 = dateFormat1.parse(yearMonth + "-" + i);
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
            calendar.clear();
            calendar.setTime(date2);
            int k = new Integer(calendar.get(Calendar.DAY_OF_WEEK));
            if (k == 1) {// 若当天是周日
                count++;
                if (i - 6 <= 1) {
                    startEndDay[0] = 1;
                } else {
                    startEndDay[0] =  i -6;
                }
                startEndDay[1] = i;
            }
            if (k != 1 && i == days) {// 若是本月最好一天，且不是周日
                count++;
            }
            
            if (weekNo == count){
            	break;
            }
        }
        return startEndDay;
	}
	
	private void initDayLabel(Map<Integer, List<SchedualRecord>> children, PageElements ps){
		String dayPrefix = "model.schedualItems.day";
		for(int i =  1 ; i <=DEFAULT_SCHEDUAL_DAYS; i++){
			PageElement pe = ps.getElement(dayPrefix + i);
			pe.setHidden(false);
		}
		for(Integer empId : children.keySet()){
			List<SchedualRecord> schedualRecords = children.get(empId);
			int sizes = schedualRecords.size();
			
			for(int i = 1; i <= sizes; i++){
				PageElement pe = ps.getElement(dayPrefix + i);
				String label = getDayLabel(schedualRecords.get(i - 1).getDutyOn());
				pe.setLabel(label);
			}
			for(int i = sizes + 1 ; i <= DEFAULT_SCHEDUAL_DAYS; i++){
				PageElement pe = ps.getElement(dayPrefix + i);
				pe.setHidden(true);
			}
			
			break;
		}
	}
	private String getDayLabel(Date date){
		Calendar calendar = Calendar.getInstance();
		calendar.clear();
		calendar.setTime(date);
		int weekno = calendar.get(Calendar.DAY_OF_WEEK) - 1;
		String prefix = new SimpleDateFormat("yyyy-M-d").format(calendar.getTime());
		String label = prefix + "(" + weekValues.get(weekno) + ")";
		return label;
	}
	
	
//	@Rest(type="update")
//	public void generateSchedualRecord() {
//		Department example = new Department();
//		List<Department> departments = genericService.searchAll(example).getData();
//		if (departments != null){
//			for(Department dept : departments){
//				generateSchedualRecordById(dept.getId());
//			}
//		}
//		
//	}
	
	public void generateSchedualRecordById(Integer deptId, Integer acctTermId){
		SchedualDefine schedualDefine = schedualDefineService.getSchedualDefineByDeptId(deptId);
		if (schedualDefine == null) {
			log.error("没有定义排班!");
			Department dept = genericService.load(Department.class, deptId);
			throw new ApplicationException("部门没有定义预排班!", dept.getName());
		}
		
		generateSchedualRecordById(acctTermId, deptId, schedualDefine);
	}
	
	private void generateSchedualRecordById(Integer acctTermId, Integer deptId, SchedualDefine schedualDefine){
		AcctTerm acctTerm = genericService.load(AcctTerm.class, acctTermId);
		Date termDate = acctTerm.getBeginDate();
		
		Department department = new Department(deptId);
//		String yearMonth = new SimpleDateFormat("yyyy-MM").format(termDate);
		
		Schedual schedual = null;
		
		List<Employee> employees = departmentService.getValidEmployees(deptId);
		
		Calendar now = Calendar.getInstance();
		now.clear();
		now.setTime(termDate);
		int lastDay = now.getActualMaximum(Calendar.DAY_OF_MONTH);
		
		int year = now.get(Calendar.YEAR);
		int month = now.get(Calendar.MONTH);
		
		for(Employee employee : employees){
			int week = 1;
			schedual = addSchedual(acctTerm, department, week);
			
			String defaultScope = schedualDefineService.getSchedualEmployeeScope(employee.getId(), schedualDefine);
			
			for(int i = 1; i <= lastDay; i++){
				Calendar time = Calendar.getInstance();
				time.clear();
				time.set(year, month , i, 0, 0, 0);
				int weekno = time.get(Calendar.DAY_OF_WEEK) - 1; 
				
				addSchedualRecord(schedual, employee, time, weekno, schedualDefine, defaultScope);
				
				if (weekno == 0 && i < lastDay){ //0 is sunday
					week ++;
					schedual = addSchedual(acctTerm, department, week);
				}
			}
		}
	}
	
	private void addSchedual(Schedual schedual, Employee employee, SchedualItem schedualItem){
		if (schedualItem != null){
			for(int i = 1; i<= DEFAULT_SCHEDUAL_DAYS; i ++){
				int month = schedual.getAcctTerm().getMonth();
				int week = schedual.getWeek();
				int year = schedual.getAcctTerm().getYear();
				
				Date dutyOn = null;
				SchedualRecord sr = new SchedualRecord();
				sr.setEmployee(employee);
				sr.setDutyOn(dutyOn);
				sr.setParent(schedual);
				schedual.getChildren().add(sr);
			}
		}
	}
	
//	private Date getDate(int year, int month, int week, int i){
//		
//	}
	
	private SchedualRecord addSchedualRecord(Schedual schedual, Employee employee, Calendar time, int weekno, SchedualDefine schedualDefine, String defaultScope){
		SchedualRecord example = new SchedualRecord();
		example.setParent(schedual);
		example.setEmployee(employee);
		example.setDutyOn(time.getTime());
		example = genericService.searchAll(example).getData(0);
		if (example == null){
			SchedualRecord child = new SchedualRecord();
			child.setParent(schedual);
			child.setEmployee(employee);
			child.setDutyOn(time.getTime());
			
			if (defaultScope != null){
				String strWeekNbrs = schedualDefine.getWeekNbrs();
				if (strWeekNbrs != null){
					boolean included = false;
					String[] weekNbrs = strWeekNbrs.split(",");
					for(int i= 0 ; i< weekNbrs.length; i ++){
						int len = Schedual.WEEK_PREFIX.length();
						String temp = weekNbrs[i].trim();
						int nbr = Integer.valueOf(temp.substring(len));
						if (nbr == weekno) {
							included = true;
							break;
						}
					}
					if (included){
						child.setScope(defaultScope);
					}
				}
			}
			
			return genericService.add(child);
		}
		return example;
	}
	
	private Schedual addSchedual(AcctTerm acctTerm, Department department, Integer week){
		Schedual example = new Schedual();
//		example.setMonth(month);
		example.setAcctTerm(acctTerm);
		example.setDepartment(department);
		example.setWeek(week);
		example = genericService.searchAll(example).getData(0);
		if (example == null){
			Schedual schedual = new Schedual();
//			schedual.setMonth(month);
			schedual.setAcctTerm(acctTerm);
			schedual.setDepartment(department);
			schedual.setCreated(new Date());
			schedual.setWeek(week);
			schedual = genericService.add(schedual);
			return schedual;
		}
		return example;
	}

	public List<SchedualRecord> getEmployeeSchedualRecords(Employee emp, Date timeFrom, Date timeTo) {
		List<SchedualRecord> ret = new ArrayList<SchedualRecord>();
//		final String hql = String.format("FROM SchedualRecord a WHERE a.dutyOn BETWEEN '%s' AND '%s' AND a.employee.id = %s", 
//				DateUtils.format(timeFrom, "yyyy-MM-dd HH:mm:ss"), DateUtils.format(timeTo, "yyyy-MM-dd HH:mm:ss"), emp.getId()
//				);
		
		final String hql = "FROM " + SchedualRecord.class.getName() + " a WHERE a.dutyOn BETWEEN ? AND ? AND a.employee.id = ?"; 
		QLInfo qlInfo = new QLInfo(hql, true, timeFrom, timeTo, emp.getId());
//		QLInfo qlInfo = new QLInfo(hql, true);
		ret.addAll(genericService.searchByQl(qlInfo, false).getData());
		return ret;
	}

	public List<TimeRange> scope2TimeRanges(Date day, String scope, String mark) {
		List<TimeRange> ret = new ArrayList<TimeRange>();
		
		if (StringUtils.isEmpty(scope)) {
			return ret;
		}
		
		String[] scopeArray = scope.split(SCOPE_SEPARATOR_REGEXS[1]);//split ,
		if (scopeArray != null && scopeArray.length > 0) {
			for (String s : scopeArray) {
				ret.add(scope2TimeRange(day, s, mark));
			}
		}
		
		
		return ret;
	}

	
	//0930-1430
	private TimeRange scope2TimeRange(Date day, String s, String mark) {
		final String[] timeRangeStrs = s.split(SCOPE_SEPARATOR_REGEXS[0]);//split -
		final String fromHourAndMinute = timeRangeStrs[0];
		final String toHourAndMinute = timeRangeStrs[1];
		
		final int fromHour = Integer.parseInt(fromHourAndMinute.substring(0, 2));
		final int fromMinute = Integer.parseInt(fromHourAndMinute.substring(2, 4));

		final int toHour = Integer.parseInt(toHourAndMinute.substring(0, 2));
		final int toMinute = Integer.parseInt(toHourAndMinute.substring(2, 4));
		
		TimeRange timeRange = new TimeRange(day, fromHour, fromMinute, toHour, toMinute);
		timeRange.setMarkAndType(mark, 0);
		return timeRange;
	}
	
}
